﻿using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Net.Security;
using System.Net.Sockets;
using System.Threading;

namespace System.Net.Mail.Pop3
{
    /// <summary>
    /// Allows applications to receive e-mail by using the Post Office Protocol version 3 (POP3).
    /// </summary>
    public class Pop3Client : Component, IDisposable
    {

        #region Fields

        private TcpClient tcpClient;
        private StreamWriter streamWriter;
        private StreamReader streamReader;
        private Stream stream;
        private string host;
        private int port;
        private Pop3State state;
        private int timeout;
        private bool isBusy;
        private SendOrPostCallback onLoginCompletedDelegate;
        private SendOrPostCallback onGetMailboxStatusCompletedDelegate;
        private SendOrPostCallback onGetMessageListingCompletedDelegate;
        private SendOrPostCallback onGetMessageCompletedDelegate;
        private SendOrPostCallback onDeleteMessageCompletedDelegate;
        private SendOrPostCallback onResetMailboxCompletedDelegate;
        private SendOrPostCallback onGetUidListingCompletedDelegate;
        private bool cancel;

        #endregion

        #region Constructors

        public Pop3Client(string host)
        {
            if (host == null)
            {
                throw new ArgumentNullException();
            }
            if (host.Length == 0)
            {
                throw new ArgumentException();
            }
            this.host = host;
            this.port = 110;
            state = Pop3State.NotConnected;
            timeout = 1000;
            isBusy = false;
            InitializeDelegates();
        }

        public Pop3Client(string host, int port)
        {
            if (host == null)
            {
                throw new ArgumentNullException();
            }
            if (host.Length == 0)
            {
                throw new ArgumentException();
            }
            if (port < 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            this.host = host;
            this.port = port;
            state = Pop3State.NotConnected;
            timeout = 1000;
            isBusy = false;
            InitializeDelegates();
        }

        #endregion

        #region Methods

        #region Public

        public void Connect()
        {
            tcpClient = new TcpClient(host, port);
            tcpClient.ReceiveTimeout = timeout;
            tcpClient.SendTimeout = timeout;
            if (EnableSsl)
            {
                stream = new SslStream(tcpClient.GetStream());
                ((SslStream)stream).AuthenticateAsClient(host);
            }
            else
            {
                stream = tcpClient.GetStream();
            }
            streamWriter = new StreamWriter(stream);
            streamReader = new StreamReader(stream);
            if (!streamReader.ReadLine().StartsWith("+OK", StringComparison.OrdinalIgnoreCase))
            {
                throw new Pop3Exception();
            }
            state = Pop3State.Authorization;
        }

        public void Disconnect()
        {
            if (tcpClient != null)
            {
                streamWriter.Close();
                streamWriter.Dispose();
                streamReader.Close();
                streamReader.Dispose();
                stream.Close();
                stream.Dispose();
                tcpClient.Close();
                tcpClient = null;
            }
        }

        public void LogOn(string userName, string password)
        {
            if (state != Pop3State.Authorization)
            {
                throw new InvalidOperationException();
            }
            if (userName == null)
            {
                throw new ArgumentNullException();
            }
            if (userName.Length == 0)
            {
                throw new ArgumentException();
            }
            if (password == null)
            {
                throw new ArgumentNullException();
            }
            if (password.Length == 0)
            {
                throw new ArgumentException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            string response = "";
            streamWriter.WriteLine("USER " + userName);
            response = streamReader.ReadLine();
            if (response.StartsWith("-ERR", StringComparison.OrdinalIgnoreCase))
            {
                throw new Pop3Exception(response.Substring(4));
            }
            streamWriter.WriteLine("PASS " + password);
            response = streamReader.ReadLine();
            if (response.StartsWith("-ERR", StringComparison.OrdinalIgnoreCase))
            {
                throw new Pop3Exception(response.Substring(4));
            }
            state = Pop3State.Transaction;
        }

        public void LogOnAsync(string userName, string password)
        {
            if (state != Pop3State.Authorization)
            {
                throw new InvalidOperationException();
            }
            if (userName == null)
            {
                throw new ArgumentNullException();
            }
            if (userName.Length == 0)
            {
                throw new ArgumentException();
            }
            if (password == null)
            {
                throw new ArgumentNullException();
            }
            if (password.Length == 0)
            {
                throw new ArgumentException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation("loginAsync");
            LogOnWorkEventHandler workerDelegate = new LogOnWorkEventHandler(LogOnWorker);
            isBusy = true;
            workerDelegate.BeginInvoke(userName, password, asyncOperation, null, null);
        }

        public MailboxStatusResponse GetMailboxStatus()
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            streamWriter.WriteLine("STAT");
            string response = GetResponse();
            string[] responseArray = response.Split(' ');
            return new MailboxStatusResponse(long.Parse(responseArray[1], CultureInfo.InvariantCulture), long.Parse(responseArray[2], CultureInfo.InvariantCulture));
        }

        public void GetMailboxStatusAsync()
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation("statAsync");
            GetMailboxStatusWorkEventHandler workerDelegate = new GetMailboxStatusWorkEventHandler(GetMailboxStatusWorker);
            isBusy = true;
            workerDelegate.BeginInvoke(asyncOperation, null, null);
        }

        public MessageListingResponse GetMessageListing()
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            streamWriter.WriteLine("LIST");
            string[] response = MultiLineParser();
            Dictionary<long, long> responseDictionary = new Dictionary<long, long>();
            foreach (string line in response)
            {
                string[] responseArray = line.Split(' ');
                responseDictionary.Add(long.Parse(responseArray[0], CultureInfo.InvariantCulture), long.Parse(responseArray[1], CultureInfo.InvariantCulture));
            }
            return new MessageListingResponse(responseDictionary);
        }

        public void GetMessageListingAsync()
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation("listAsync");
            GetMessageListingWorkEventHandler workerDelegate = new GetMessageListingWorkEventHandler(GetMessageListingWorker);
            isBusy = true;
            workerDelegate.BeginInvoke(asyncOperation, null, null);
        }

        public MessageListingResponse GetMessageListing(long index)
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (index.Equals(null))
            {
                throw new ArgumentNullException();
            }
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            streamWriter.WriteLine("LIST " + index);
            string response = GetResponse();
            Dictionary<long, long> responseDictionary = new Dictionary<long, long>();
            string[] responseArray = response.Split(' ');
            responseDictionary.Add(long.Parse(responseArray[0], CultureInfo.InvariantCulture), long.Parse(responseArray[1], CultureInfo.InvariantCulture));
            return new MessageListingResponse(responseDictionary);
        }

        public void GetMessageListingAsync(long index)
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (index.Equals(null))
            {
                throw new ArgumentNullException();
            }
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation("listAsync" + index);
            GetMessageListingWorkEventHandler workerDelegate = new GetMessageListingWorkEventHandler(GetMessageListingWorker);
            isBusy = true;
            workerDelegate.BeginInvoke(asyncOperation, null, null);
        }

        public MailMessage GetMessage(long index)
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (index.Equals(null))
            {
                throw new ArgumentNullException();
            }
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            string[] response = MultiLineParser();
            MailMessage mailMessage = null;
            StringReader stringReader = null;
            try
            {
                stringReader = new StringReader(String.Join("\n", response));
                mailMessage = MailMessageMimeParser.ParseMessage(stringReader);
            }
            catch
            {
                throw;
            }
            finally
            {
                if (stringReader != null)
                {
                    stringReader.Close();
                    streamReader.Dispose();
                }
            }
            return mailMessage;
        }

        public void GetMessageAsync(long index)
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (index.Equals(null))
            {
                throw new ArgumentNullException();
            }
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation("retrAsync");
            GetMessageWorkEventHandler workerDelegate = new GetMessageWorkEventHandler(GetMessageWorker);
            isBusy = true;
            workerDelegate.BeginInvoke(index, asyncOperation, null, null);
        }

        public void DeleteMessage(long index)
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (index.Equals(null))
            {
                throw new ArgumentNullException();
            }
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            streamWriter.WriteLine("DELE " + index);
            GetResponse();
        }

        public void DeleteMessageAsync(long index)
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (index.Equals(null))
            {
                throw new ArgumentNullException();
            }
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation("deleAsync");
            DeleteMessageWorkEventHandler workerDelegate = new DeleteMessageWorkEventHandler(DeleteMessageWorker);
            isBusy = true;
            workerDelegate.BeginInvoke(index, asyncOperation, null, null);
        }

        public void CheckConnection()
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            streamWriter.WriteLine("NOOP");
        }

        public void ResetMailbox()
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            streamWriter.WriteLine("RSET");
            GetResponse();
        }

        public void ResetMailboxAsync()
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation("rsetAsync");
            ResetMailboxWorkEventHandler workerDelegate = new ResetMailboxWorkEventHandler(ResetMailboxWorker);
            isBusy = true;
            workerDelegate.BeginInvoke(asyncOperation, null, null);
        }

        public void Quit()
        {
            if (state == Pop3State.NotConnected)
            {
                throw new InvalidOperationException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            streamWriter.WriteLine("QUIT");
            string response = streamReader.ReadLine();
            switch (state)
            {
                case Pop3State.Authorization:
                    Disconnect();
                    break;
                case Pop3State.Transaction:
                    if (response.StartsWith("-ERR", StringComparison.OrdinalIgnoreCase))
                    {
                        if (response.Length > 4)
                        {
                            throw new Pop3Exception(response.Substring(4));
                        }
                        else
                        {
                            throw new Pop3Exception();
                        }
                    }
                    state = Pop3State.Update;
                    break;
                case Pop3State.Update:
                    Disconnect();
                    if (response.StartsWith("-ERR", StringComparison.OrdinalIgnoreCase))
                    {
                        if (response.Length > 4)
                        {
                            throw new Pop3Exception(response.Substring(4));
                        }
                        else
                        {
                            throw new Pop3Exception();
                        }
                    }
                    break;
            }
        }

        public void CancelAsync()
        {
            if (!isBusy)
            {
                throw new InvalidOperationException();
            }
            cancel = true;
        }

        public UidListingResponse GetUidListing()
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            streamWriter.WriteLine("UIDL");
            string[] response = MultiLineParser();
            Dictionary<long, string> responseDictionary = new Dictionary<long, string>();
            foreach (string line in response)
            {
                string[] responseArray = line.Split(' ');
                responseDictionary.Add(long.Parse(responseArray[0], CultureInfo.InvariantCulture), responseArray[1]);
            }
            return new UidListingResponse(responseDictionary);
        }

        public UidListingResponse GetUidListing(long index)
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (index.Equals(null))
            {
                throw new ArgumentNullException();
            }
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            streamWriter.WriteLine("UIDL " + index);
            string response = GetResponse();
            Dictionary<long, string> responseDictionary = new Dictionary<long, string>();
            string[] responseArray = response.Split(' ');
            responseDictionary.Add(long.Parse(responseArray[0], CultureInfo.InvariantCulture), responseArray[1]);
            return new UidListingResponse(responseDictionary);
        }

        public void GetUidListingAsync()
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation("uidlAsync");
            GetUidListingWorkEventHandler workerDelegate = new GetUidListingWorkEventHandler(GetMessageListingWorker);
            isBusy = true;
            workerDelegate.BeginInvoke(asyncOperation, null, null);
        }

        public void UGetUidListingAsync(long index)
        {
            if (state != Pop3State.Transaction)
            {
                throw new InvalidOperationException();
            }
            if (index.Equals(null))
            {
                throw new ArgumentNullException();
            }
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (isBusy)
            {
                throw new InvalidOperationException();
            }
            AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation("uidlAsync" + index);
            GetUidListingWorkEventHandler workerDelegate = new GetUidListingWorkEventHandler(GetMessageListingWorker);
            isBusy = true;
            workerDelegate.BeginInvoke(asyncOperation, null, null);
        }

        #endregion

        #region Private

        private void DeleteMessageCompletion(Exception error, bool cancelled, AsyncOperation asyncOperation)
        {
            AsyncCompletedEventArgs e = new AsyncCompletedEventArgs(error, cancelled, null);
            asyncOperation.PostOperationCompleted(onDeleteMessageCompletedDelegate, e);
        }

        private void DeleteMessageWorker(long index, AsyncOperation asyncOperation)
        {
            Exception e = null;
            if (!cancel)
            {
                try
                {
                    DeleteMessage(index);
                }
                catch (Exception ex)
                {
                    e = ex;
                }
            }
            DeleteMessageCompletion(e, cancel, asyncOperation);
        }

        private void GetMessageListingCompletion(Exception error, bool cancelled, MessageListingResponse response, AsyncOperation asyncOperation)
        {
            GetMessageListingCompletedEventArgs e = new GetMessageListingCompletedEventArgs(error, cancelled, response);
            asyncOperation.PostOperationCompleted(onGetMessageListingCompletedDelegate, e);
        }

        private void GetMessageListingWorker(AsyncOperation asyncOperation)
        {
            Exception e = null;
            MessageListingResponse res = null;
            if (!cancel)
            {
                if (asyncOperation.UserSuppliedState.ToString().Length == 9)
                {
                    try
                    {
                        res = GetMessageListing();
                    }
                    catch (Exception ex)
                    {
                        e = ex;
                    }
                }
                else
                {
                    long msg = long.Parse(asyncOperation.UserSuppliedState.ToString().Substring(9), CultureInfo.InvariantCulture);
                    try
                    {
                        res = GetMessageListing(msg);
                    }
                    catch (Exception ex)
                    {
                        e = ex;
                    }
                }
            }
            GetMessageListingCompletion(e, cancel, res, asyncOperation);
        }

        private void LogOnCompletion(Exception error, bool cancelled, AsyncOperation asyncOperation)
        {
            AsyncCompletedEventArgs e = new AsyncCompletedEventArgs(error, cancelled, null);
            asyncOperation.PostOperationCompleted(onLoginCompletedDelegate, e);
        }

        private void LogOnWorker(string userName, string password, AsyncOperation asyncOperation)
        {
            Exception e = null;
            if (!cancel)
            {
                try
                {
                    LogOn(userName, password);
                }
                catch (Exception ex)
                {
                    e = ex;
                }
            }
            LogOnCompletion(e, cancel, asyncOperation);
        }

        private void GetMessageCompletion(Exception error, bool cancelled, MailMessage message, AsyncOperation asyncOperation)
        {
            GetMessageCompletedEventArgs e = new GetMessageCompletedEventArgs(error, cancelled, message);
            asyncOperation.PostOperationCompleted(onGetMessageCompletedDelegate, e);
        }

        private void GetMessageWorker(long index, AsyncOperation asyncOperation)
        {
            Exception e = null;
            MailMessage res = null;
            if (!cancel)
            {
                try
                {
                    res = GetMessage(index);
                }
                catch (Exception ex)
                {
                    e = ex;
                }
            }
            GetMessageCompletion(e, cancel, res, asyncOperation);
        }

        private void ResetMailboxCompletion(Exception error, bool cancelled, AsyncOperation asyncOperation)
        {
            AsyncCompletedEventArgs e = new AsyncCompletedEventArgs(error, cancelled, null);
            asyncOperation.PostOperationCompleted(onResetMailboxCompletedDelegate, e);
        }

        private void ResetMailboxWorker(AsyncOperation asyncOperation)
        {
            Exception e = null;
            if (!cancel)
            {
                try
                {
                    ResetMailbox();
                }
                catch (Exception ex)
                {
                    e = ex;
                }
            }
            ResetMailboxCompletion(e, cancel, asyncOperation);
        }

        private void GetMailboxStatusCompletion(Exception error, bool cancelled, MailboxStatusResponse response, AsyncOperation asyncOperation)
        {
            GetMailboxStatusCompletedEventArgs e = new GetMailboxStatusCompletedEventArgs(error, cancelled, response);
            asyncOperation.PostOperationCompleted(onGetMailboxStatusCompletedDelegate, e);
        }

        private void GetMailboxStatusWorker(AsyncOperation asyncOperation)
        {
            Exception e = null;
            MailboxStatusResponse res = null;
            if (!cancel)
            {
                try
                {
                    res = GetMailboxStatus();
                }
                catch (Exception ex)
                {
                    e = ex;
                }
            }
            GetMailboxStatusCompletion(e, cancel, res, asyncOperation);
        }

        private void GetUidListingCompletion(Exception error, bool cancelled, UidListingResponse response, AsyncOperation asyncOperation)
        {
            GetUidListingCompletedEventArgs e = new GetUidListingCompletedEventArgs(error, cancelled, response);
            asyncOperation.PostOperationCompleted(onGetUidListingCompletedDelegate, e);
        }

        private void GetUidListingWorker(AsyncOperation asyncOperation)
        {
            Exception e = null;
            UidListingResponse res = null;
            if (!cancel)
            {
                if (asyncOperation.UserSuppliedState.ToString().Length == 9)
                {
                    try
                    {
                        res = GetUidListing();
                    }
                    catch (Exception ex)
                    {
                        e = ex;
                    }
                }
                else
                {
                    long msg = long.Parse(asyncOperation.UserSuppliedState.ToString().Substring(9), CultureInfo.InvariantCulture);
                    try
                    {
                        res = GetUidListing(msg);
                    }
                    catch (Exception ex)
                    {
                        e = ex;
                    }
                }
            }
            GetUidListingCompletion(e, cancel, res, asyncOperation);
        }

        private string[] MultiLineParser()
        {
            List<string> list = new List<string>();
            while (true)
            {
                string line = GetResponse();
                if (line == ".")
                {
                    break;
                }
                list.Add(line);
            }
            return list.ToArray();
        }

        private string GetResponse()
        {
            string res = streamReader.ReadLine();
            if (res.StartsWith("-ERR", StringComparison.OrdinalIgnoreCase))
            {
                if (res.Length > 4)
                {
                    throw new Pop3Exception(res.Substring(4));
                }
                else
                {
                    throw new Pop3Exception();
                }
            }
            return res;
        }

        #endregion

        #region protected

        protected void InitializeDelegates()
        {
            onDeleteMessageCompletedDelegate = new SendOrPostCallback(OnDeleteMessageCompleted);
            onGetMessageListingCompletedDelegate = new SendOrPostCallback(OnGetMessageListingCompleted);
            onLoginCompletedDelegate = new SendOrPostCallback(OnLogOnCompleted);
            onGetMessageCompletedDelegate = new SendOrPostCallback(OnGetMessageCompleted);
            onResetMailboxCompletedDelegate = new SendOrPostCallback(OnResetMailboxCompleted);
            onGetMailboxStatusCompletedDelegate = new SendOrPostCallback(OnGetMailboxStatusCompleted);
            onGetUidListingCompletedDelegate = new SendOrPostCallback(OnGetUidListingCompleted);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                GC.SuppressFinalize(this);
            }
            Disconnect();
            base.Dispose(disposing);
        }

        protected void OnDeleteMessageCompleted(object operationState)
        {
            isBusy = false;
            cancel = false;
            if (DeleteMessageCompleted != null)
            {
                DeleteMessageCompleted(this, operationState as AsyncCompletedEventArgs);
            }
        }

        protected void OnGetMessageListingCompleted(object operationState)
        {
            isBusy = false;
            cancel = false;
            if (GetMessageListingCompleted != null)
            {
                GetMessageListingCompleted(this, operationState as GetMessageListingCompletedEventArgs);
            }
        }

        protected void OnLogOnCompleted(object operationState)
        {
            isBusy = false;
            cancel = false;
            if (LogOnCompleted != null)
            {
                LogOnCompleted(this, operationState as AsyncCompletedEventArgs);
            }
        }

        protected void OnGetMessageCompleted(object operationState)
        {
            isBusy = false;
            cancel = false;
            if (GetMessageCompleted != null)
            {
                GetMessageCompleted(this, operationState as GetMessageCompletedEventArgs);
            }
        }

        protected void OnResetMailboxCompleted(object operationState)
        {
            isBusy = false;
            cancel = false;
            if (ResetMailboxCompleted != null)
            {
                ResetMailboxCompleted(this, operationState as AsyncCompletedEventArgs);
            }
        }

        protected void OnGetMailboxStatusCompleted(object operationState)
        {
            isBusy = false;
            cancel = false;
            if (GetMailboxStatusCompleted != null)
            {
                GetMailboxStatusCompleted(this, operationState as GetMailboxStatusCompletedEventArgs);
            }
        }

        protected void OnGetUidListingCompleted(object operationState)
        {
            isBusy = false;
            cancel = false;
            if (GetUidListingCompleted != null)
            {
                GetUidListingCompleted(this, operationState as GetUidListingCompletedEventArgs);
            }
        }

        #endregion

        #endregion

        #region Properties

        public bool EnableSsl
        {
            get;
            set;
        }

        public string Host
        {
            get
            {
                return host;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException();
                }
                if (value.Length == 0)
                {
                    throw new ArgumentException();
                }
                if (state != Pop3State.NotConnected)
                {
                    throw new InvalidOperationException();
                }
                host = value;
            }
        }

        public int Port
        {
            get
            {
                return port;
            }
            set
            {
                if (value < 0)
                {
                    throw new ArgumentOutOfRangeException();
                }
                if (state != Pop3State.NotConnected)
                {
                    throw new InvalidOperationException();
                }
                port = value;
            }
        }

        public int Timeout 
        { 
            get
            {
                return timeout;
            }
            set
            {
                if (value < 0)
                {
                    throw new ArgumentOutOfRangeException();
                }
                if (state != Pop3State.NotConnected)
                {
                    throw new InvalidOperationException();
                }
                timeout = value;
            }
        }

        public Pop3State State
        {
            get
            {
                return state;
            }
        }

        public bool IsBusy
        {
            get
            {
                return isBusy;
            }
        }

        #endregion

        #region Events

        public event EventHandler<AsyncCompletedEventArgs> LogOnCompleted;

        public event EventHandler<GetMailboxStatusCompletedEventArgs> GetMailboxStatusCompleted;

        public event EventHandler<GetMessageListingCompletedEventArgs> GetMessageListingCompleted;

        public event EventHandler<GetMessageCompletedEventArgs> GetMessageCompleted;

        public event EventHandler<AsyncCompletedEventArgs> DeleteMessageCompleted;

        public event EventHandler<AsyncCompletedEventArgs> ResetMailboxCompleted;

        public event EventHandler<GetUidListingCompletedEventArgs> GetUidListingCompleted;

        #endregion

        #region Delegates

        private delegate void LogOnWorkEventHandler(string userName, string password, AsyncOperation asyncOperation);

        private delegate void GetMailboxStatusWorkEventHandler(AsyncOperation asyncOperation);

        private delegate void GetMessageListingWorkEventHandler(AsyncOperation asyncOperation);

        private delegate void GetMessageWorkEventHandler(long index, AsyncOperation asyncOperation);

        private delegate void DeleteMessageWorkEventHandler(long index, AsyncOperation asyncOperation);

        private delegate void ResetMailboxWorkEventHandler(AsyncOperation asyncOperation);

        private delegate void GetUidListingWorkEventHandler(AsyncOperation asyncOperation);

        #endregion

    }
}
